﻿/* 
Copyright (c) 2014 Eric Follea

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

*/
using Facet4Windows.Plugin;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using System.Xml.Serialization;

namespace Facet4Windows
{


    public class EnvironmentsFactory
    {
        private static XNamespace configNS = XNamespace.None;
        public Environments from(Stream stream)
        {
            XDocument doc = XDocument.Load(stream);

            return from(doc.Element(configNS + "config"));
        }


        public Environments from(XElement config)
        {
            IEnumerable<EnvironmentFacet> environments = getFacets(config.Element(configNS + "facets"));
            IEnumerable<XElement> configurations = config.Element(configNS + "modules").Elements();

            Environments envs = new Environments { environments = environments.ToList() };

            envs.environments.ForEach(env => env.Entries.ForEach(entry => entry.facet = env));

            return envs;
        }

        public IEnumerable<EnvironmentFacet> getFacets(XElement facets)
        {


            return (from env in facets.Elements(configNS + "facet")
                    select new EnvironmentFacet
                    {
                        Name = ((string)env.Element(configNS + "name")) ?? "N/A",
                        Activated = (bool?)env.Element(configNS + "activated") ?? true,
                        Entries = (from entry in env.Descendants(configNS + "entry")
                                   select new EnvironmentEntry
                                   {
                                       Name = ((string)entry.Attribute(configNS + "name")) ?? "N/A",
                                       Activated = (bool?)entry.Attribute(configNS + "activated") ?? true,
                                       Uri = new Uri(entry.Value)
                                   }).ToList()
                    }
        );

        }


    }
    public class Environments
    {
        private Facet4Windows.Plugin.PluginController plugins;
        [XmlAttribute()]
        public List<EnvironmentFacet> environments { get; set; }

        public Environments()
        {
            this.environments = new List<EnvironmentFacet>();
            this.plugins = new Facet4Windows.Plugin.PluginController("plugins");
        }
        public void load()
        {
            this.plugins.load();
        }
        private IDictionary<String, IEnvironmentController> controllers { get { return this.plugins.controllers; } }

        public void env_OnDeactivated(EnvironmentFacet sender)
        {
            var all = controllers.Values.ToList();
            all.ForEach(controller => controller.beginUpdate());
            foreach (var entry in sender.Entries.Where(e => e.Activated))
            {
                IEnvironmentController controler;
                if (controllers.TryGetValue(entry.Uri.Scheme, out controler)) controler.Deactivate(entry.Uri);
                else Console.WriteLine(entry.Uri.Scheme + " not binded to any controller : no plugin found for scheme");
            }

            all.ForEach(controller => controller.endUpdate());
        }

        public void env_OnActivated(EnvironmentFacet sender)
        {
            var all = controllers.Values.ToList();
            all.ForEach(controller => controller.beginUpdate());
            foreach (var entry in sender.Entries.Where(e => e.Activated))
            {
                IEnvironmentController controler;
                if (controllers.TryGetValue(entry.Uri.Scheme, out controler)) controler.Activate(entry.Uri);
                else Console.WriteLine(entry.Uri.Scheme + " not binded to any controller : no plugin found for scheme");
            }
            all.ForEach(controller => controller.endUpdate());
        }

        public void start()
        {
            IDictionary<String, EnvironmentEntry> activated = new Dictionary<String, EnvironmentEntry>();
            //foreach (var env in this.environments.Where(x => x.Activated))
            //{
            //    foreach (var entry in env.Entries.Where(ent => ent.Activated && this.controllers.ContainsKey(ent.Uri.Scheme)))
            //    {
            //        activated.Add(entry.Uri.Scheme, entry);
            //    }
            //}


            //foreach (var kvp in activated)
            //{
            //    this.controllers[kvp.Key].Entries.Add(kvp.Value);


            //}
            this.environments.Where(env => env.Activated).ToList().ForEach(env => env_OnActivated(env));
        }

        public void stop()
        {
            this.controllers.Values.ToList().ForEach(ctrl => ctrl.Close());
        }
    }
    public delegate void InteractEventHandler(EnvironmentFacet sender);

    public class EnvironmentFacet : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        // NotifyPropertyChanged will raise the PropertyChanged event, 
        // passing the source property that is being updated.
        public void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public event InteractEventHandler OnActivated;
        public event InteractEventHandler OnDeactivated;
        public void switchState()
        {
            Activated = !Activated;
            InteractEventHandler handler = Activated ? OnActivated : OnDeactivated;

            if (handler != null)
            {
                handler(this);
            }
        }

        private String name;
        private bool activated;

        public String Name { get { return name; } set { name = value; NotifyPropertyChanged("Name"); } }
        public bool Activated { get { return activated; } set { activated = value; NotifyPropertyChanged("Activated"); } }
        public List<EnvironmentEntry> Entries { get; set; }
    }

    public class EnvironmentEntry
    {
        public EnvironmentFacet facet { get; set; }
        public String Name { get; set; }
        public Uri Uri { get; set; }
        public bool Activated { get; set; }
    }
}
